//
// Created by wxd on 2025/7/11.
// 封装着色器对象
//

#pragma once

#include <iostream>
#include <string>
#include "../../thirdParty/include/glad/glad.h"
#include <glm/glm.hpp>
#include <variant>
#include <unordered_map>
#include <vector>
#include <glm/gtc/type_ptr.hpp>
#include <type_traits>
#include "BaseShader.h"
#include <unordered_map>
#include "../utils/RenderEngineLogger.h"
#include <sstream>

// 支持的uniform类型
using UniformValue = std::variant<
    int,
    unsigned int,
    float,
    double,
    bool,
    glm::vec2,
    glm::vec3,
    glm::vec4,
    glm::mat2,
    glm::mat3,
    glm::mat4
>;

class Shader : public BaseShader {
public:
    // 内部类：定义着色器uniform参数类
    struct UniformDefine {
        GLuint location;       // uniform变量在GPU中的location
        UniformValue data;     // uniform参数数据
        std::string name;      // uniform参数名称
    };


    Shader(const char* vertexPath, const char* fragmentPath);
    Shader(const std::string& vertexPath, const std::string& fragmentPath);

    Shader(const Shader&) = delete;
    Shader& operator=(const Shader&) = delete;

    ~Shader();

    GLuint getProgramId() const;

    /**
     * 着色器程序对象的激活绑定以及解除绑定
     */
    void bindProgram() const;
    void unbindProgram() const;

    /**
     * 释放着色器程序
     */
    void deleteProgram() const;

    /**
     * 获取顶点着色器中的location
     */
    GLuint getLocation(const std::string& name) const;
    GLuint getLocation(GLuint programId, const std::string& name) const;

    /**
     * 单uniform数据传递
     */
    template<typename T>
    void setUniform(GLuint program, const std::string& name, const T& value) {
        GLuint location = -1;
        if (m_UniformLocationCacheMap.find(name) != m_UniformLocationCacheMap.end()) {
            location = m_UniformLocationCacheMap[name];
        } else {
            location = glGetUniformLocation(program, name.c_str());
        }
        if (location == -1) {
            std::cerr << "Warning: Uniform '" << name << "' not found in program " << program << std::endl;
            return;
        }

        if (m_UniformParamsMap.find(name) == m_UniformParamsMap.end()) {
            UniformDefine uniformDef = { location, value, name };
            m_UniformParamsMap[name] = uniformDef;
        }

        setUniformImpl(location, value);
    }

    /**
     * 单uniform数据传输
     */
    template<typename T>
    void setUniform(const std::string& name, const T& value) {
        GLuint location = -1;
        if (m_UniformLocationCacheMap.find(name) != m_UniformLocationCacheMap.end()) {
            location = m_UniformLocationCacheMap[name];
        } else {
            location = glGetUniformLocation(mProgramId, name.c_str());
        }
        if (location == -1) {
            std::cerr << "Warning: Uniform '" << name << "' not found in program " << mProgramId << std::endl;
            return;
        }

        if (m_UniformParamsMap.find(name) == m_UniformParamsMap.end()) {
            UniformDefine uniformDef = { location, value, name };
            m_UniformParamsMap[name] = uniformDef;
        }

        setUniformImpl(location, value);
    }

    /**
     * 多uniform数据传输
     */
    void setUniforms(const std::unordered_map<std::string, UniformValue>& uniformParams) {
        if (uniformParams.empty()) {
            return;
        }

        for (const auto& [name, value] : uniformParams) {
            std::visit([&](auto&& arg) {
                using T = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<T, int>
                                || std::is_same_v<T, unsigned int>
                                || std::is_same_v<T, float>
                                || std::is_same_v<T, double>
                                || std::is_same_v<T, bool>
                                || std::is_same_v<T, glm::mat2>
                                || std::is_same_v<T, glm::mat3>
                                || std::is_same_v<T, glm::mat4>
                                || std::is_same_v<T, glm::vec2>
                                || std::is_same_v<T, glm::vec3>
                                || std::is_same_v<T, glm::vec4>) {
                    setUniform(name, arg);
                } else {
                    std::cerr << "Unknown uniform type for: " << name << std::endl;
                }
            }, value);
        }
    }

    /**
     * 输出当前着色器所有已经uniform到GPU的变量
     * debug模式下才对外提供这个输出uniform参数的日志函数
     */
#ifdef LOG_DEBUG
    void printUniforms() const {
        if (m_UniformParamsMap.empty()) {
            return;
        }

        auto temp = Log.msg(m_ShaderTraceId).param("shaderName", m_ShaderName);

        for (const auto& [uniformName, uniformDefine] : m_UniformParamsMap) {
            std::visit([&uniformDefine, &temp](auto&& arg) {
                using T = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<T, int>
                                || std::is_same_v<T, unsigned int>
                                || std::is_same_v<T, float>
                                || std::is_same_v<T, double>
                                || std::is_same_v<T, bool>) {

                    std::string info = std::to_string(uniformDefine.location).append(":").append(std::to_string(arg));
                    temp.param(uniformDefine.name, info);

                } else if constexpr (std::is_same_v<T, glm::vec2>
                                || std::is_same_v<T, glm::vec3>
                                || std::is_same_v<T, glm::vec4>) {

                    std::ostringstream oss;
                    const int size = arg.length();
                    oss << "vec" << size << "(";
                    for (int i = 0; i < size; i++) {
                        oss << arg[i];
                        if (i > 0 && i < size - 1) {
                            oss << ",";
                        }
                    }
                    oss << ")";

                    std::string info = std::to_string(uniformDefine.location).append(":").append(oss.str());
                    temp.param(uniformDefine.name, info);
                } else if constexpr (std::is_same_v<T, glm::mat2>
                                || std::is_same_v<T, glm::mat3>
                                || std::is_same_v<T, glm::mat4>) {

                    // std::string info = std::to_string(uniformDefine.location).append(":").append(glm::to_string(arg));
                    temp.param(uniformDefine.name, "");
                } else {
                    temp.param(uniformDefine.name, "UnSupported Type");
                }
            }, uniformDefine.data);
        }

        temp.d();
    }
#endif

    void release() {
        m_UniformParamsMap.clear();
        m_UniformLocationCacheMap.clear();
    }


private:
    GLuint mProgramId;

    std::unordered_map<std::string, UniformDefine> m_UniformParamsMap {}; // 记录一个着色器需要的全部uniform参数
    std::unordered_map<std::string, GLuint> m_UniformLocationCacheMap {}; // 单独缓存location

    void setUniformImpl(const GLint location, const int value) {
        glUniform1i(location, value);
    }

    // TODO: 暂时不要使用，可能会出现数据异常
    void setUniformImpl(const GLint location, const unsigned int value) {
        glUniform1i(location, static_cast<int>(value));
    }

    void setUniformImpl(const GLint location, const float value) {
        glUniform1f(location, value);
    }

    // TODO: 暂时不要使用，可能会出现数据异常
    void setUniformImpl(const GLint location, const double value) {
        glUniform1f(location, value);
    }

    void setUniformImpl(const GLint location, const glm::vec2& value) {
        glUniform2f(location, value.x, value.y);
    }

    void setUniformImpl(const GLint location, const glm::vec3& value) {
        glUniform3f(location, value.x, value.y, value.z);
    }

    void setUniformImpl(const GLint location, const glm::vec4& value) {
        glUniform4f(location, value.x, value.y, value.z, value.w);
    }

    void setUniformImpl(const GLint location, const glm::mat2& value) {
        glUniformMatrix2fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }

    void setUniformImpl(const GLint location, const glm::mat3& value) {
        glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }

    void setUniformImpl(const GLint location, const glm::mat4& value) {
        glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }

    void setUniformImpl(const GLint location, const std::vector<int>& values) {
        glUniform1iv(location, static_cast<GLsizei>(values.size()), values.data());
    }

    void setUniformImpl(const GLint location, const std::vector<float>& values) {
        glUniform1fv(location, static_cast<GLsizei>(values.size()), values.data());
    }

    void setUniformImpl(const GLint location, const std::vector<glm::vec2>& values) {
        glUniform2fv(location, static_cast<GLsizei>(values.size()), glm::value_ptr(values[0]));
    }

    void setUniformImpl(const GLint location, const std::vector<glm::vec3>& values) {
        glUniform3fv(location, static_cast<GLsizei>(values.size()), glm::value_ptr(values[0]));
    }

    void setUniformImpl(const GLint location, const std::vector<glm::vec4>& values) {
        glUniform4fv(location, static_cast<GLsizei>(values.size()), glm::value_ptr(values[0]));
    }
};



